package org.andolphin.client.utils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * assert工具, 大多方法是从Spring的Assert, TestNG的Assert复制过来的.
 *
 * @author matianyi
 */
@SuppressWarnings("unchecked")
public class AssertHelper {
    private static final String DATE_FORMAT = "yyyy-MM-dd";

    /**
     * 断言集合c中包含except对象,用equals方法判断
     *
     * @param except 期待对象
     * @param c      集合
     */
    static public void assertContains(Object except, Collection c) {
        boolean flag = false;
        for (Object o : c) {
            if (except.equals(o)) {
                flag = true;
            }
        }
        if (!flag) {
            fail("Collection doesn't contains the except object");
        }
    }

    /**
     * 断言数组arr中包含except对象,用equals方法判断
     *
     * @param except 期待对象
     * @param arr    数组
     */
    static public void assertContains(Object except, Object[] arr) {
        boolean flag = false;
        for (Object o : arr) {
            if (except.equals(o)) {
                flag = true;
            }
        }
        if (!flag) {
            fail("Array doesn't contains the except object : " + except);
        }
    }

    /**
     * 断言集合c中包含一个对象,他的property属性为except方法判断
     *
     * @param key   期待属性
     * @param value 集合
     * @param map   期望的属性名/值
     */
    static public void assertContains(Object key, Object value, Map map) {
        Object v = map.get(key);
        if (v == null || !v.equals(value)) {
            fail("Map doesn't contains [" + key + "] whose value is [" + value + "]");
        }
    }

    /**
     * 断言集合不为空
     *
     * @param except 集合
     */
    static public void assertNotEmpty(Collection<?> except) {
        if (except == null || except.size() == 0) {
            fail("Collection is empty");
        }
    }

    /**
     * 断言数组不为空
     *
     * @param array 数组
     */
    static public void assertNotEmpty(Object[] array) {
        if (array == null || array.length == 0) {
            fail("Array is empty");
        }
    }

    /**
     * 断言Map部分为空
     *
     * @param map Map
     */
    static public void assertNotEmpty(Map<?, ?> map) {
        if (map == null || map.size() == 0) {
            fail("Map is empty");
        }
    }

    /**
     * 断言字符串有值
     *
     * @param text 字符串
     */
    static public void assertHasText(String text) {
        if (text == null || text.trim().equals("")) {
            fail("[Assertion failed] - this String argument must have text; it must not be <code>null</code>, empty, or blank");
        }
    }

    /**
     * 日期转换为字符串 格式 yyyy-MM-dd
     *
     * @param date 日期
     * @return 字符串
     */
    public String dateToString(Date date) {
        if (date == null) {
            return "";
        } else {
            SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
            return sdf.format(date);
        }
    }

    /**
     * 字符串转换为日期 格式 yyyy-MM-dd
     *
     * @param dateStr 字符串
     * @return 日期
     */
    public Date stringToDate(String dateStr) {
        if (dateStr == null || dateStr.equals("")) {
            return Calendar.getInstance().getTime();
        } else {
            SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
            try {
                return sdf.parse(dateStr);
            } catch (ParseException e) {
                return Calendar.getInstance().getTime();
            }
        }
    }

    public static void isTrue(boolean expression, String message) {
        if (!expression) {
            throw new IllegalArgumentException(message);
        }
    }

    public static void isTrue(boolean expression) {
        isTrue(expression, "[Assertion failed] - this expression must be true");
    }

    public static void isNull(Object object, String message) {
        if (object != null) {
            throw new IllegalArgumentException(message);
        }
    }

    public static void isNull(Object object) {
        isNull(object, "[Assertion failed] - the object argument must be null");
    }

    public static void notNull(Object object, String message) {
        if (object == null) {
            throw new IllegalArgumentException(message);
        }
    }

    public static void notNull(Object object) {
        notNull(object, "[Assertion failed] - this argument is required; it must not null");
    }

    public static void hasLength(String text, String message) {
        if (StringUtils.isEmpty(text.trim())) {
            throw new IllegalArgumentException(message);
        }
    }

    public static void hasLength(String text) {
        hasLength(text, "[Assertion failed] - this String argument must have length; it must not be <code>null</code> or empty");
    }

    public static void hasText(String text, String message) {
        if (StringUtils.isEmpty(text)) {
            throw new IllegalArgumentException(message);
        }
    }

    public static void hasText(String text) {
        hasText(text, "[Assertion failed] - this String argument must have text; it must not be <code>null</code>, empty, or blank");
    }

    static public void assertTrue(boolean condition, String message) {
        if (!condition) {
            failNotEquals(condition, Boolean.TRUE, message);
        }
    }

    /**
     * Asserts that a condition is true. If it isn't, an AssertionError is
     * thrown.
     *
     * @param condition the condition to evaluate
     */
    static public void assertTrue(boolean condition) {
        assertTrue(condition, null);
    }

    /**
     * Asserts that a condition is false. If it isn't, an AssertionError, with
     * the given message, is thrown.
     *
     * @param condition the condition to evaluate
     * @param message   the assertion error message
     */
    static public void assertFalse(boolean condition, String message) {
        if (condition) {
            failNotEquals(condition, Boolean.FALSE, message); // TESTNG-81
        }
    }

    /**
     * Asserts that a condition is false. If it isn't, an AssertionError is
     * thrown.
     *
     * @param condition the condition to evaluate
     */
    static public void assertFalse(boolean condition) {
        assertFalse(condition, null);
    }

    /**
     * Fails a test with the given message and wrapping the original exception.
     *
     * @param message   the assertion error message
     * @param realCause the original exception
     */
    static public void fail(String message, Throwable realCause) {
        AssertionError ae = new AssertionError(message);
        ae.initCause(realCause);

        throw ae;
    }

    /**
     * Fails a test with the given message.
     *
     * @param message the assertion error message
     */
    static public void fail(String message) {
        throw new AssertionError(message);
    }

    /**
     * Fails a test with no message.
     */
    static public void fail() {
        fail(null);
    }

    /**
     * Asserts that two objects are equal. If they are not, an AssertionError,
     * with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertEquals(Object actual, Object expected, String message) {
        if ((expected == null) && (actual == null)) {
            return;
        }
        if ((expected != null) && expected.equals(actual)) {
            return;
        }
        failNotEquals(actual, expected, message);
    }

    /**
     * Asserts that two objects are equal. If they are not, an AssertionError is
     * thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEquals(Object actual, Object expected) {
        assertEquals(actual, expected, null);
    }

    /**
     * Asserts that two Strings are equal. If they are not, an AssertionError is
     * thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEquals(String actual, String expected) {
        assertEquals(actual, expected, null);
    }

    /**
     * Asserts that two Strings are equal. If they are not, an AssertionError is
     * thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEqualsIgnoreCase(String actual, String expected) {
        assertEquals(actual, expected, null);
    }

    /**
     * Asserts that two doubles are equal concerning a delta. If they are not,
     * an AssertionError, with the given message, is thrown. If the expected
     * value is infinity then the delta value is ignored.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param delta    the absolute tolerate value value between the actual and
     *                 expected value
     * @param message  the assertion error message
     */
    static public void assertEquals(double actual, double expected, double delta, String message) {
        // handle infinity specially since subtracting to infinite values gives
        // NaN and the
        // the following test fails
        if (Double.isInfinite(expected)) {
            if (!(expected == actual)) {
                failNotEquals(actual, expected, message);
            }
        } else if (!(Math.abs(expected - actual) <= delta)) { // Because
            // comparison
            // with NaN
            // always
            // returns false
            failNotEquals(actual, expected, message);
        }
    }

    /**
     * Asserts that two doubles are equal concerning a delta. If they are not,
     * an AssertionError is thrown. If the expected value is infinity then the
     * delta value is ignored.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param delta    the absolute tolerate value value between the actual and
     *                 expected value
     */
    static public void assertEquals(double actual, double expected, double delta) {
        assertEquals(actual, expected, delta, null);
    }

    /**
     * Asserts that two floats are equal concerning a delta. If they are not, an
     * AssertionError, with the given message, is thrown. If the expected value
     * is infinity then the delta value is ignored.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param delta    the absolute tolerate value value between the actual and
     *                 expected value
     * @param message  the assertion error message
     */
    static public void assertEquals(float actual, float expected, float delta, String message) {
        // handle infinity specially since subtracting to infinite values gives
        // NaN and the
        // the following test fails
        if (Float.isInfinite(expected)) {
            if (!(expected == actual)) {
                failNotEquals(actual, expected, message);
            }
        } else if (!(Math.abs(expected - actual) <= delta)) {
            failNotEquals(actual, expected, message);
        }
    }

    /**
     * Asserts that two floats are equal concerning a delta. If they are not, an
     * AssertionError is thrown. If the expected value is infinity then the
     * delta value is ignored.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param delta    the absolute tolerate value value between the actual and
     *                 expected value
     */
    static public void assertEquals(float actual, float expected, float delta) {
        assertEquals(actual, expected, delta, null);
    }

    /**
     * Asserts that two longs are equal. If they are not, an AssertionError,
     * with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertEquals(long actual, long expected, String message) {
        assertEquals(new Long(actual), new Long(expected), message);
    }

    /**
     * Asserts that two longs are equal. If they are not, an AssertionError is
     * thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEquals(long actual, long expected) {
        assertEquals(actual, expected, null);
    }

    /**
     * Asserts that two booleans are equal. If they are not, an AssertionError,
     * with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertEquals(boolean actual, boolean expected, String message) {
        assertEquals(Boolean.valueOf(actual), Boolean.valueOf(expected), message);
    }

    /**
     * Asserts that two booleans are equal. If they are not, an AssertionError
     * is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEquals(boolean actual, boolean expected) {
        assertEquals(actual, expected, null);
    }

    /**
     * Asserts that two bytes are equal. If they are not, an AssertionError,
     * with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertEquals(byte actual, byte expected, String message) {
        assertEquals(new Byte(actual), new Byte(expected), message);
    }

    /**
     * Asserts that two bytes are equal. If they are not, an AssertionError is
     * thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEquals(byte actual, byte expected) {
        assertEquals(actual, expected, null);
    }

    /**
     * Asserts that two chars are equal. If they are not, an
     * AssertionFailedError, with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertEquals(char actual, char expected, String message) {
        assertEquals(new Character(actual), new Character(expected), message);
    }

    /**
     * Asserts that two chars are equal. If they are not, an AssertionError is
     * thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEquals(char actual, char expected) {
        assertEquals(actual, expected, null);
    }

    /**
     * Asserts that two shorts are equal. If they are not, an
     * AssertionFailedError, with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertEquals(short actual, short expected, String message) {
        assertEquals(new Short(actual), new Short(expected), message);
    }

    /**
     * Asserts that two shorts are equal. If they are not, an AssertionError is
     * thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEquals(short actual, short expected) {
        assertEquals(actual, expected, null);
    }

    /**
     * Asserts that two ints are equal. If they are not, an
     * AssertionFailedError, with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertEquals(int actual, int expected, String message) {
        assertEquals(new Integer(actual), new Integer(expected), message);
    }

    /**
     * Asserts that two ints are equal. If they are not, an AssertionError is
     * thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEquals(int actual, int expected) {
        assertEquals(actual, expected, null);
    }

    /**
     * Asserts that an object isn't null. If it is, an AssertionError is thrown.
     *
     * @param object the assertion object
     */
    static public void assertNotNull(Object object) {
        assertNotNull(object, null);
    }

    /**
     * Asserts that an object isn't null. If it is, an AssertionFailedError,
     * with the given message, is thrown.
     *
     * @param object  the assertion object
     * @param message the assertion error message
     */
    static public void assertNotNull(Object object, String message) {
        assertTrue(object != null, message);
    }

    /**
     * Asserts that an object is null. If it is, an AssertionError, with the
     * given message, is thrown.
     *
     * @param object the assertion object
     */
    static public void assertNull(Object object) {
        assertNull(object, null);
    }

    /**
     * Asserts that an object is null. If it is not, an AssertionFailedError,
     * with the given message, is thrown.
     *
     * @param object  the assertion object
     * @param message the assertion error message
     */
    static public void assertNull(Object object, String message) {
        assertTrue(object == null, message);
    }

    /**
     * Asserts that two objects refer to the same object. If they do not, an
     * AssertionFailedError, with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertSame(Object actual, Object expected, String message) {
        if (expected == actual) {
            return;
        }
        failNotSame(actual, expected, message);
    }

    /**
     * Asserts that two objects refer to the same object. If they do not, an
     * AssertionError is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertSame(Object actual, Object expected) {
        assertSame(actual, expected, null);
    }

    /**
     * Asserts that two objects do not refer to the same objects. If they do, an
     * AssertionError, with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertNotSame(Object actual, Object expected, String message) {
        if (expected == actual) {
            failSame(actual, expected, message);
        }
    }

    /**
     * Asserts that two objects do not refer to the same object. If they do, an
     * AssertionError is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertNotSame(Object actual, Object expected) {
        assertNotSame(actual, expected, null);
    }

    static private void failSame(Object actual, Object expected, String message) {
        String formatted = "";
        if (message != null) {
            formatted = message + " ";
        }
        fail(formatted + "expected not same with:<" + expected + "> but was same:<" + actual + ">");
    }

    static private void failNotSame(Object actual, Object expected, String message) {
        String formatted = "";
        if (message != null) {
            formatted = message + " ";
        }
        fail(formatted + "expected same with:<" + expected + "> but was:<" + actual + ">");
    }

    static private void failNotEquals(Object actual, Object expected, String message) {
        fail(format(actual, expected, message));
    }

    static String format(Object actual, Object expected, String message) {
        String formatted = "";
        if (null != message) {
            formatted = message + " ";
        }

        return formatted + "expected:<" + expected + "> but was:<" + actual + ">";
    }

    /**
     * Asserts that two collections contain the same elements in the same order.
     * If they do not, an AssertionError is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEquals(Collection<Object> actual, Collection<Object> expected) {
        assertEquals(actual, expected, null);
    }

    /**
     * Asserts that two collections contain the same elements in the same order.
     * If they do not, an AssertionError, with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertEquals(Collection actual, Collection expected, String message) {
        if (actual == expected)
            return;

        if ((actual == null) || (expected == null)) {
            if (message != null)
                fail(message);
            else
                fail("Arrays not equal: " + expected + " and " + actual);
        }

        assert actual != null;
        assert expected != null;
        assertEquals(actual.size(), expected.size(), message + ": lists don't have the same size");

        Iterator actIt = actual.iterator();
        Iterator expIt = expected.iterator();
        int i = -1;
        while (actIt.hasNext() && expIt.hasNext()) {
            i++;
            Object e = expIt.next();
            Object a = actIt.next();
            String errorMessage = message == null ? "Lists differ at element [" + i + "]: " + e + " != " + a : message + ": Lists differ at element [" + i
                    + "]: " + e + " != " + a;

            assertEquals(a, e, errorMessage);
        }
    }

    /**
     * Asserts that two arrays contain the same elements in the same order. If
     * they do not, an AssertionError, with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertEquals(Object[] actual, Object[] expected, String message) {
        if (actual == expected)
            return;

        if ((actual == null) || (expected == null)) {
            if (message != null)
                fail(message);
            else
                fail("Arrays not equal: " + Arrays.toString(expected) + " and " + Arrays.toString(actual));
        }
        assertEquals(Arrays.asList(actual), Arrays.asList(expected), message);
    }

    /**
     * Asserts that two arrays contain the same elements in no particular order.
     * If they do not, an AssertionError, with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertEqualsNoOrder(Object[] actual, Object[] expected, String message) {
        if (actual == expected)
            return;

        if ((actual == null) || (expected == null)) {
            if (message != null)
                fail(message);
            else
                fail("Arrays not equal: " + Arrays.toString(expected) + " and " + Arrays.toString(actual));
        }

        Collection<Object> actualCollection = new HashSet<Object>();
        actualCollection.addAll(Arrays.asList(actual));

        Collection<Object> expectedCollection = new HashSet<Object>();
        expectedCollection.addAll(Arrays.asList(expected));

        assertEquals(actualCollection, expectedCollection, message);
    }

    /**
     * Asserts that two arrays contain the same elements in the same order. If
     * they do not, an AssertionError is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEquals(Object[] actual, Object[] expected) {
        assertEquals(actual, expected, null);
    }

    /**
     * Asserts that two arrays contain the same elements in no particular order.
     * If they do not, an AssertionError is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEqualsNoOrder(Object[] actual, Object[] expected) {
        assertEqualsNoOrder(actual, expected, null);
    }

    /**
     * Asserts that two arrays contain the same elements in the same order. If
     * they do not, an AssertionError is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     */
    static public void assertEquals(final byte[] actual, final byte[] expected) {
        assertEquals(actual, expected, "");
    }

    /**
     * Asserts that two arrays contain the same elements in the same order. If
     * they do not, an AssertionError, with the given message, is thrown.
     *
     * @param actual   the actual value
     * @param expected the expected value
     * @param message  the assertion error message
     */
    static public void assertEquals(final byte[] actual, final byte[] expected, final String message) {
        if (expected == actual) {
            return;
        }
        if (null == expected) {
            fail("expected a null array, but not null found. " + message);
        }
        if (null == actual) {
            fail("expected not null array, but null found. " + message);
        }

        assert actual != null;
        assert expected != null;
        assertEquals(actual.length, expected.length, "arrays don't have the same size. " + message);

        for (int i = 0; i < expected.length; i++) {
            if (expected[i] != actual[i]) {
                fail("arrays differ firstly at element [" + i + "]; " + "expected value is <" + expected[i] + "> but was <" + actual[i] + ">. " + message);
            }
        }
    }

    /**
     * 断言c中包括类型为clazz的元素
     *
     * @param c     集合
     * @param clazz 类型
     */
    public static void assertContainsType(Collection c, Class clazz) {
        if (null == c) {
            fail("Collection is null.");
        }
        if (null == clazz) {
            fail("Class is null.");
        }

        assert c != null;
        assert clazz != null;

        boolean flag = false;

        for (Object o : c) {
            if (o.getClass().equals(clazz)) {
                flag = true;
            }
        }

        if (!flag) {

            fail("Collection doesn't contain a element whoes type is " + clazz.getName());
        }
    }

    /**
     * 断言c中包括类型为clazz的元素
     *
     * @param array 集合
     * @param clazz 类型
     */
    public static void assertContainsType(Object[] array, Class clazz) {
        assertContainsType(Arrays.asList(array), clazz);
    }

    /**
     * 断言指定的文字中有指定的片段
     *
     * @param str  要断言的文字
     * @param text 包含的文字
     */
    public static void assertContainsText(String str, String text) {
        assertNotNull(str);
        String strLower = str.toLowerCase();
        String textLower = text.toLowerCase();
        if (!strLower.contains(textLower)) {
            fail("String [" + str + "] does not contain [" + text + "]");
        }
    }
}
